using System;
using System.IO;
using System.Text;
using System.Data;
using System.Diagnostics;
using System.ServiceModel;
using System.Configuration;
using System.ComponentModel;
using System.ServiceProcess;
using System.Collections.Generic;
using System.ServiceModel.Description;
using Microsoft.Practices.EnterpriseLibrary.Logging;

namespace Services.WCF.Hosting
{
    [HostServiceAttribute("Host")]
    public partial class HostServiceBase : ServiceBase
    {
        private ServiceHost _host;

        private ISubscriber _subscriber;

        protected ISubscriber Subscriber
        {
            get { return _subscriber; }
        }

        ServiceConfigurationHandler configuration;

        protected ServiceConfigurationHandler Configuration
        {
            get { return configuration; }
        }

        public HostServiceBase()
        {
            HostServiceAttribute attribute = (HostServiceAttribute)this.GetType().GetCustomAttributes(typeof(HostServiceAttribute), true)[0];
            this.ServiceName = attribute.DisplayName;
            configuration = (ServiceConfigurationHandler)ConfigurationManager.GetSection("hostConfiguration");
            InitializeComponent();
            
        }

        protected override void OnStart(string[] args)
        {
            System.Threading.Thread mainThread = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(ThreadMain));
            mainThread.Start(args);
        }

        protected virtual void ThreadMain(object args)
        {
            InitializeHostController();
            foreach (ServiceConfigurationElement service in configuration.Services)
            {
                if (service.Enabled)
                    Create(service.Name);
            }
        }

        protected override void OnStop()
        {
            string[] domains = Host.Domains;
            foreach (string domain in domains)
            {
                LogEntry entry = new LogEntry();
                entry.EventId = 1;
                entry.Priority = -1;
                entry.Title = "Stopping Service";
                entry.Categories.Add("General");
                entry.Message = String.Format("Service Disconnecting Host {0}", domain);
                entry.Severity = System.Diagnostics.TraceEventType.Information;
                Logger.Write(entry);
                Remove(domain);
            }
        }
              
        #region Application domains management 

        protected virtual void Create(String name)
        {
            
            try
            {
                LogEntry entry = new LogEntry();
                entry.EventId = 1;
                entry.Priority = -1;
                entry.Title = "Create";
                entry.Categories.Add("General");
                entry.Message = String.Format("Loading Service {0}", Path.GetFileName(name));
                entry.Severity = System.Diagnostics.TraceEventType.Information;
                Logger.Write(entry);

                Uri[] baseAddresses;
                ServiceConfigurationHandler configuration = (ServiceConfigurationHandler)ConfigurationManager.GetSection("hostConfiguration");
                ServiceConfigurationElement service = configuration.Services[name];

                //string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, String.Format("{0}.config", name));
                if (String.IsNullOrEmpty(service.ConfigFilePath))
                {
                    String uri = String.IsNullOrEmpty(service.EndPoint) ? configuration.EndPoint : service.EndPoint;
                    baseAddresses = new Uri[] { new Uri(String.Format(uri, System.Net.Dns.GetHostName(), service.Name)) };
                }
                else
                {
                    baseAddresses = new Uri[0];
                }

                Type type = Type.GetType(configuration.Subscriber);
                ISubscriber monitor = InitializeMonitor();
                Host.Start(service.Name, monitor, service.AssemblyQualifiedName, new object[] { }, service.ConfigFilePath, baseAddresses);
                if (Host.IsValidDomain(service.Name))
                {
                    entry = new LogEntry();
                    entry.EventId = 1;
                    entry.Priority = -1;
                    entry.Title = "Create";
                    entry.Categories.Add("General");
                    entry.Message = String.Format("Running {0}...", service.Name);
                    entry.Severity = System.Diagnostics.TraceEventType.Information;
                    Logger.Write(entry);
                }
            }
            catch (Exception ex)
            {
                LogEntry entry = new LogEntry();
                entry.EventId = 1;
                entry.Priority = 10;
                entry.Title = "Create";
                entry.Categories.Add("Error");
                entry.Message = ex.ToString();
                entry.Severity = System.Diagnostics.TraceEventType.Error;
                Logger.Write(entry);
            }
        }

        protected virtual void Remove(String name)
        {
            //string name = GetDomainNameFromFile(assemblyFile);
            if (Host.IsValidDomain(name))
            {
                try
                {
                    Host.Stop(name);

                    LogEntry entry = new LogEntry();
                    entry.EventId = 1;
                    entry.Priority = 5;
                    entry.Title = "Remove";
                    entry.Categories.Add("Warning");
                    entry.Message = String.Format("{0} Unloaded", name);
                    entry.Severity = System.Diagnostics.TraceEventType.Warning;
                    Logger.Write(entry);
                    
                }
                catch (Exception ex)
                {
                    LogEntry entry = new LogEntry();
                    entry.EventId = 1;
                    entry.Priority = 10;
                    entry.Title = "Remove";
                    entry.Categories.Add("Error");
                    entry.Message = ex.ToString();
                    entry.Severity = System.Diagnostics.TraceEventType.Error;
                    Logger.Write(entry);
                }
            }
        }

        protected virtual void Update(String name)
        {
            Remove(name);
            Create(name);
        }

        protected static String GetDomainNameFromFile(String assemblyFile)
        {
            return Path.GetFileNameWithoutExtension(assemblyFile).Split('.')[0];
        }
        #endregion

        #region HostController

        protected virtual void InitializeHostController()
        {
            _subscriber = InitializeMonitor();
            try
            {
                HostController singletonInstance = InitializeController();
                singletonInstance.OnStart += new ResumeHosting(Create);
                singletonInstance.OnStop += new StopHosting(Remove);
                singletonInstance.OnRecycle += new RecycleHosting(Update);
                singletonInstance.OnEnumServices = delegate()
                {
                    List<HostControllerEntry> entries = new List<HostControllerEntry>();
                    foreach (ServiceConfigurationElement service in configuration.Services)
                    {
                        HostControllerEntry entry = new HostControllerEntry();
                        Status state = Status.NONE;
                        switch (service.Enabled)
                        {
                            case true:
                                state = Status.ENABLED;
                                break;

                            case false:
                                state = Status.DISABLED;
                                break;
                        }

                        switch (Host.IsValidDomain(service.Name))
                        {
                            case true:
                                state |= Status.ACTIVE;
                                break;

                            case false:
                                state = Status.INACTIVE;
                                break;
                        }

                        entry.Name = service.Name;
                        entry.State = state;
                        entries.Add(entry);

                    }
                    return entries.ToArray();
                };

                HostServiceAttribute attribute = (HostServiceAttribute)this.GetType().GetCustomAttributes(typeof(HostServiceAttribute), true)[0];
                Uri[] baseAddresses = new Uri[] { new Uri(String.Format(configuration.EndPoint, System.Net.Dns.GetHostName(),attribute.Name))};

                _host = new ServiceHost(singletonInstance, baseAddresses);
                foreach (ServiceEndpoint endpoint in _host.Description.Endpoints)
                {
                    _subscriber.HostInformation(AppDomain.CurrentDomain, new HostEventArgs(String.Format("Opening {0}", endpoint.Address)));
                    endpoint.Behaviors.Add(_subscriber);
                }
                _host.Open();
            }
            catch (Exception ex)
            {
                _subscriber.HostException(AppDomain.CurrentDomain, ex);
            }
            finally
            {
                AppDomain.CurrentDomain.DomainUnload += delegate(Object sender, EventArgs eArgs)
                {
                    this.Close();
                };
            }
        }

        protected virtual ISubscriber InitializeMonitor()
        {
            ISubscriber subscriber = null;
            if (String.IsNullOrEmpty(configuration.Subscriber))
            {
                subscriber = new Monitor();
            }
            else
            {
                Type type = Type.GetType(configuration.Subscriber);
                subscriber = (ISubscriber)Activator.CreateInstance(type);
            }
            return subscriber;
        }

        protected virtual HostController InitializeController()
        {
            Type type = null;
            HostController controller = null;
            if (String.IsNullOrEmpty(configuration.HostControler))
            {
                controller = new HostController();
                type = controller.GetType();

            }
            else
            {
                type = Type.GetType(configuration.HostControler);
                controller = (HostController)Activator.CreateInstance(type);
            }
            _subscriber.HostInformation(AppDomain.CurrentDomain, new HostEventArgs(String.Format("Starting {0}", type.Name)));
            return controller;
        }

        protected virtual void Close()
        {
            try
            {
                if (_host != null && (_host.State != CommunicationState.Closed || _host.State != CommunicationState.Closing))
                {
                    foreach (ServiceEndpoint endpoint in _host.Description.Endpoints)
                    {
                        _subscriber.HostInformation(AppDomain.CurrentDomain, new HostEventArgs(String.Format("Closing {0}", endpoint.Address)));
                        endpoint.Behaviors.Clear();
                    }
                    _host.Close();
                }
            }
            catch (System.Exception ex)
            {
                _subscriber.HostException(AppDomain.CurrentDomain, ex);
            }
        }

        #endregion

    }
}
